feat(errors): @constructive-io/errors package + server code normalization & class-based masking - #1415
Merged
Merged
Conversation
Add a standalone, zero-dependency @constructive-io/errors package (canonical registry, parse/format/classify, i18n, type-safe factory), make pgpm consume it, and normalize DB codes into GraphQL extensions with class-based masking.
Contributor
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
Collect every constructive-db EXCEPTION/THROW code (287) into a generated registry layer with public/internal class, HTTP hint, and message (public copy seeded from dashboard catalogs). Curated typed entries override generated ones. Reproducible via scripts/generate-registry.py from a committed audit snapshot.
Re-audit constructive-db (253 commits newer): +18 codes, -16 removed. Commit the reproducible audit extractor (audit-db-errors.py) and update the committed inventory snapshot + generated registry accordingly.
- Replace the legacy client DataError system (graphql/query client) with a thin adapter over @constructive-io/errors: parseGraphQLError/createError now produce a canonical ConstructiveError; drop the duplicated DataErrorType, PG_ERROR_CODES and message string-matching. Add toError() to the package. - parse() now trusts an explicit producer class (DB DETAIL.class / server extensions.class) over the registry fallback, so newly-raised codes are classified correctly before a registry refresh. - Add transport/client codes (NETWORK_ERROR, TIMEOUT_ERROR, BAD_USER_INPUT, VALIDATION_FAILED, UNKNOWN_ERROR) and 17 public auth/resource codes migrated from the server allowlist to the curated registry. - Server graphile masking: replace the hand-maintained SAFE_ERROR_CODES allowlist with registry classify(), keeping only GraphQL framework protocol codes as an explicit set. - Wire @constructive-io/errors into graphql-query; update codegen re-exports, tests and README.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Introduces
@constructive-io/errors— a zero-dependency, canonical Constructive error system — and makes the GraphQL server, the GraphQL client, and pgpm consume it instead of their own duplicated error catalogs. One package is now the single source of truth for error codes, their public/internal class, message copy (i18n), and cross-source parsing (PostgreSQL, GraphQL, message, SQLSTATE).packages/errors(new)parse(anyError)→{ code, context, class, known, sqlState, ... }with precedence:DETAILJSON ({code, context, class}from the DBerrors.raise_errorhelper),extensions.code,ALL_CAPSmessage token (+ positional args),23505→UNIQUE_VIOLATION, etc.).parse()now trusts an explicit producer class (DETAIL.class/extensions.class) over the registry fallback, so a newly-raised code is classified correctly even before a registry refresh. Invalid class values are ignored (fall back to the registry). Unknown codes remain fail-closed → internal.format(code, context, locale)does interpolation + localization;ConstructiveErrorcarriescode / message / class / http / context;errors.*factories are derived from the registry.scripts/audit-db-errors.py+scripts/generate-registry.py.Server (
graphql/server)maskErrornormalizes any error into the canonical shape and lifts the real code intoextensions.code/class/context(fixes the original bug where DB errors reached clients with emptyextensions).SAFE_ERROR_CODESset is deleted; only the 4 GraphQL framework protocol codes remain as an explicit set:The 17 genuinely-public auth/resource codes that were only on the old allowlist (e.g.
USER_NOT_AUTHENTICATED,ACCOUNT_LOCKED,SSO_*_DISABLED,INVALID_SIGNATURE,UPLOAD_MIMETYPE) were migrated into the curated registry aspublic, and SQLSTATE constraint errors already resolve to public codes viaparse()— so this shrink causes zero masking regression (verified: all 82 former semantic allowlist codes classifypublic).Client (
graphql/query+graphql/codegen)DataErrorsystem is replaced (no back-compat):DataError,DataErrorType,PG_ERROR_CODES,isDataError, and the message string-matching / constraint regexes are gone.graphql/query/src/client/error.tsis now a thin adapter over@constructive-io/errors:execute.tskeeps its behavior: abort →TIMEOUT_ERROR, fetch failure →NETWORK_ERROR, 401/403/404 → canonical factories, GraphQL/body errors → canonical parser.NETWORK_ERROR,TIMEOUT_ERROR,BAD_USER_INPUT,VALIDATION_FAILED,UNKNOWN_ERROR.@constructive-io/graphql-query.pgpm (
pgpm/types)error-factoryre-exportserrors/makeErrorfrom the package; call sites and messages unchanged.Notes
@constructive-io/errorsgoing forward.errors.raise_error(code, context, class)runtime helper (#2510), build-time AST helper (#2512), and funneling generated raises through it (#2537) — these emit the structuredDETAILthis parser consumes.packages/errors(28),graphql/query(18),graphql/server(120),graphql/codegen(358) all pass under Node 22.Link to Devin session: https://app.devin.ai/sessions/f4d3ce9225894883ba9cfc9ed2f0ba7e
Requested by: @pyramation